package com.fingard.dsp.bank.directbank.cib03;

import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.util.EncryptUtil;
import cfca.sadk.x509.certificate.X509Cert;
import com.fingard.constant.Format;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.text.StringHelper;
import com.fingard.util.TransIDHelper;
import sun.misc.BASE64Encoder;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;

public class CIB03Base extends DirectBase {
    protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }

    private static TransIDHelper idHelper = new TransIDHelper(1, 999);

    protected String getQryTransID() {
        Date tmpDateNow = new Date();
        return Format.DateTime12Year2Format.format(tmpDateNow) + idHelper.getNextIDStr();
    }

    /**
     * 生成待签名参数
     * 
     * @param params
     * @return
     */
    public String generateParamStr(Map<String, String> params) {
    	List<String> paramList = new ArrayList<String>();
    	for (String key : params.keySet()) {
    		if (!"mac".equals(key)) {
    			String val = params.get(key);
    			if(StringHelper.isNullOrEmpty(val) || "null".equals(val)){
    				continue;
    			}
    			paramList.add(key + "=" + val);
    		}
		}
    	if (paramList.size() == 0) {
			return null;
		}
		Collections.sort(paramList);
		StringBuilder sb = new StringBuilder();
		sb.append(paramList.get(0));
		for (int i = 1; i < paramList.size(); i++) {
			sb.append("&").append(paramList.get(i));
		}
		WriteBankLogLn2("待签名参数:\r\n" + sb.toString());
		return sb.toString();
    }
    
    /**
     * RSA私钥签名
     * 
     * @param params
     * @param actItem
     * @return
     * @throws Exception
     */
    public String generateMAC(Map<String, String> params, ActSetItem actItem) throws Exception {
    	KeyStore ks = KeyStore.getInstance("PKCS12");
		FileInputStream fis = new FileInputStream(actItem.ownKeyStorePath);
		char[] keyPassword = null;
		if (StringHelper.hasAnyChar(actItem.ownKeyPassword)) {
			keyPassword = actItem.ownKeyPassword.toCharArray();
		}
		ks.load(fis, keyPassword);
		fis.close();

		Enumeration<?> enumAlias = ks.aliases();
		String keyAlias = null;
		if (enumAlias.hasMoreElements()) {
			keyAlias = (String) enumAlias.nextElement();
		}
		PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyPassword);
		if (privateKey == null) {
			return null;
		}




       	java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
        signature.initSign(privateKey);
        signature.update(generateParamStr(params).getBytes(getCharset()));
        byte[] signed = signature.sign();

        return  new BASE64Encoder().encode(signed).replaceAll(System.getProperty("line.separator"), "");
    }
    
    /**
     * RSA公钥验签
     * 
     * @param params
     * @return
     */
    public boolean verifyMAC(Map<String, String> params, ActSetItem actItem) {
        if (!params.containsKey("mac")) return false;
        String mac = params.get("mac");
        if (mac == null) return false;
        X509Certificate cert = null;
        FileInputStream fis=null;
        try {

            fis= new FileInputStream(actItem.oppCertFilePath);
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            cert = (X509Certificate) cf.generateCertificate(fis);
            fis.close();
            PublicKey publicKey = cert.getPublicKey();
            if (publicKey == null) {
            	return false;
            }
            
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initVerify(publicKey);
            signature.update(generateParamStr(params).getBytes(getCharset()));
            byte[] bmac = new sun.misc.BASE64Decoder().decodeBuffer(mac);
            return signature.verify(bmac);
        } catch (Exception e) {
            return false;
        }finally {
            if(fis!=null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
	/**
	 * 给敏感信息加密
	 * @param msgContent 明文
	 * @return 密文
	 * @throws Exception
	 */
	public static String encryptByCFCA(ActSetItem actItem, String msgContent) {
        InputStream fis = null;
	    try {
			final String deviceName = JCrypto.JSOFT_LIB;
			JCrypto.getInstance().initialize(deviceName, null);
			Session session = JCrypto.getInstance().openSession(deviceName);
			final String c12FileText = actItem.ownCertFilePath;

			
			File file = new File(c12FileText);
			if (file.exists()) {
				fis = new FileInputStream(c12FileText);
			} else {
				Class<? extends CIB03Base> clz = CIB03Base.class;
				ClassLoader cl = clz.getClassLoader();
				
				URL url = cl.getResource(c12FileText);
				if (url == null)
					throw new FileNotFoundException();
				
				fis = cl.getResourceAsStream(c12FileText);
			}
			// 加密算法："RSA/ECB/PKCS1PADDING"
			final Mechanism mechanism = new Mechanism(Mechanism.RSA_PKCS);
			X509Cert cert = new X509Cert(fis);
			// RSA公钥加密：长度通常限定（1024密钥为117字节内；2048密钥为245字节内）
			byte[] base64Bytes = EncryptUtil.encrypt(mechanism, cert.getPublicKey(), msgContent.getBytes("UTF8"), session);
			return new String(base64Bytes);
		} catch (Exception e) {
			return null;
		}finally {
           if(fis!=null) {
               try {
                   fis.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }
	}
	
    /**
     * 发送银行
     * 
     * @param p_reqData
     * @param p_for
     * @return
     * @throws Exception
     */
    public String sendDataToBank(String p_reqData, String p_for)  {
    	String postUrl = bankFront.getUrl(p_for);
		WriteBankLogLn2("服务地址:" + postUrl);
		WriteBankLogStep2(p_reqData);
		
    	URL url = null;
        HttpURLConnection conn = null;
        OutputStream outStream = null;
        InputStream inStream = null;
        try {
        	url = new URL(postUrl);
            conn = (HttpURLConnection) url.openConnection();
            if (url.getProtocol().equals("https")) {
            	HttpsURLConnection con = (HttpsURLConnection)conn;
            	TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                    public void checkClientTrusted(
                            java.security.cert.X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(
                            java.security.cert.X509Certificate[] certs, String authType) {
                    }
                }};
                SSLContext sc = SSLContext.getInstance("SSL");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                con.setSSLSocketFactory(sc.getSocketFactory());
                con.setHostnameVerifier(new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
            }
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            conn.setRequestProperty("Server", "Apache-Coyote/1.1");
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            outStream = conn.getOutputStream();
            outStream.write(p_reqData.getBytes(getCharset()));
            outStream.flush();
            conn.connect();
            inStream = conn.getInputStream();
            ByteArrayOutputStream outArrStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];// 2k
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
            	outArrStream.write(buffer, 0, len);
            }
            String tmpStrRet = new String(outArrStream.toByteArray(), getCharset());
            outArrStream.close();
            WriteBankLogStep3(tmpStrRet);
            return tmpStrRet;
        } catch (Exception ex) {
        	WriteBankLogLn(ex);
        } finally {
        	try{
            if (inStream != null) inStream.close();
        	}catch(Exception e){
        		WriteBankLogLn(e);
        	}
        	try{
            if (outStream != null) outStream.close();
        	}catch(Exception e){
        		WriteBankLogLn(e);
        	}
        	try{
            if (conn != null) conn.disconnect();
        	}catch(Exception e){
        		WriteBankLogLn(e);
        	}
        }
        return null;
    }
}
